The half second of map that should not have been there - #226
Merged
Conversation
OWNER: *"terus kejar yg 0.5 detik itu."*
Outside MiniPay the world map was visible under the gate for about half a
second, and no amount of React could fix it: the map is in the PRERENDERED
HTML, so it paints before a single line of JavaScript has run. That is
deliberate and worth keeping — it is what /game's LCP optimisation buys, and
MiniPay players, who never see the gate, keep the whole benefit.
So the fix is the only thing that can run earlier than React: an inline script
in the document. When the gate is certain to appear it marks <html> before the
first paint and CSS hides the home screen; when it is not, it does nothing at
all and the map paints exactly as it does today.
It marks <html> rather than appending an overlay, and that was measured, not
assumed. The overlay version was written first and Next's hydration — which
reconciles the children of <body> — deleted the node at 261ms, roughly 600ms
before the gate was ready to cover anything. An attribute React never rendered
is not part of that reconciliation and survives.
Hiding by `visibility` rather than removing: the map's image request stays
intact, so a player who skips the gate gets the map immediately instead of
paying for it twice.
Measured after the change, polling every 30ms for 5s:
inside MiniPay no mark, home visible at 102ms (untouched)
new player outside mark 144→509ms, home NEVER visible, gate 547ms
returning (skipped once) no mark, home visible at 95ms (no black wait)
This is a THIRD copy of the MiniPay rule, and the cost was accepted knowingly:
every branch in the script can only decide "do not veil", so a drift between
the copies costs a non-MiniPay player half a second of map and can never reach
a MiniPay player at all. The 3s dead-man timer matters as much — if React never
arrives, the mark lifts by itself rather than leaving somebody on a black
screen.
Verified: tsc clean, build unchanged at 142/242 kB, 41 assertions in test:privy
including one that runs in CI on the default path, plus test:streak-ui,
test:season-ui, test:pass-cards, check:copy, check:cssvars, check:market and
audit. Lint 35.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017A764RdnwpyWnG7uCNhMiQ
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Outside MiniPay the world map was visible under the gate for about half a second. No amount of React could fix it: the map is in the prerendered HTML, so it paints before a single line of JavaScript has run. That is deliberate and worth keeping — it is what
/game's LCP optimisation buys, and MiniPay players (who never see the gate) keep the whole benefit.So the fix is the only thing that can run earlier than React: an inline script in the document. When the gate is certain to appear it marks
<html>before the first paint and CSS hides the home screen. When it is not, it does nothing at all and the map paints exactly as it does today.Why an attribute and not an overlay
The overlay version was written first, and measured. Next's hydration reconciles the children of
<body>and deleted the node at 261 ms — roughly 600 ms before the gate was ready to cover anything, so the map was on screen from 323 ms to 882 ms anyway. An attribute React never rendered is not part of that reconciliation and survives.Hiding by
visibilityrather than removing, so the map's image request stays intact: a player who skips the gate gets the map immediately instead of paying for it twice.Measured, polling every 30 ms for 5 s
MiniPay is untouched. A returning player pays no black screen for a gate they will never be shown.
The cost, stated plainly
This is a third copy of the MiniPay rule, and it was accepted knowingly. Every branch in the script can only decide "do not veil", so a drift between the copies costs a non-MiniPay player half a second of map and can never reach a MiniPay player at all.
The 3 s dead-man timer matters as much as the conditions: if React never arrives — a chunk 404, a JS error — the mark lifts by itself rather than leaving somebody on a black screen.
Testing
Generated by Claude Code